#include "sdk.h"
#include "wx/wxprec.h"
#include "wx/wx.h"
#include "wx/log.h"
#include <wx/event.h>
#include <wx/frame.h>
#include <wx/intl.h>
#include <wx/menu.h>
#include <wx/menuitem.h>
#include <wx/string.h>
#include "wx/statline.h"
#include "keybinder.h"
#include "menuutils.h"
#include "wx/config.h"
#include "wx/tokenzr.h"

#pragma implementation "keybinder.h"
#error 无法载入

IMPLEMENT_CLASS( wxKeyProfile, wxKeyBinder )
IMPLEMENT_CLASS( wxKeyBinder, wxObject )
IMPLEMENT_CLASS( wxBinderEvtHandler, wxEvtHandler )
BEGIN_EVENT_TABLE( wxBinderEvtHandler, wxEvtHandler )
  EVT_KEY_DOWN( wxBinderEvtHandler::OnChar )
END_EVENT_TABLE()

IMPLEMENT_CLASS( wxKeyMonitorTextCtrl, wxTextCtrl )
BEGIN_EVENT_TABLE( wxKeyMonitorTextCtrl, wxTextCtrl )
  EVT_KEY_DOWN( wxKeyMonitorTextCtrl::OnKey )
  EVT_KEY_UP( wxKeyMonitorTextCtrl::OnKey )
END_EVENT_TABLE()

IMPLEMENT_CLASS( wxKeyConfigPanel, wxPanel )
BEGIN_EVENT_TABLE( wxKeyConfigPanel, wxPanel )
  EVT_TEXT( wxKEYBINDER_KEY_FIELD_ID, wxKeyConfigPanel::OnKeyPressed )
  EVT_TEXT( wxKEYBINDER_KEYPROFILES_ID, wxKeyConfigPanel::OnProfileEditing )
  EVT_COMBOBOX( wxKEYBINDER_KEYPROFILES_ID, wxKeyConfigPanel::OnProfileSelected )
  EVT_LISTBOX( wxKEYBINDER_BINDINGS_BOX_ID, wxKeyConfigPanel::OnBindingSelected )
  EVT_TREE_SEL_CHANGED( wxKEYBINDER_COMMANDS_BOX_ID, wxKeyConfigPanel::OnTreeCommandSelected )
  EVT_COMBOBOX( wxKEYBINDER_CATEGORIES_ID, wxKeyConfigPanel::OnCategorySelected )
  EVT_LISTBOX( wxKEYBINDER_COMMANDS_BOX_ID, wxKeyConfigPanel::OnListCommandSelected )
  EVT_BUTTON( wxID_APPLY, wxKeyConfigPanel::OnApplyChanges )
  EVT_BUTTON( wxKEYBINDER_ASSIGN_KEY_ID, wxKeyConfigPanel::OnAssignKey )
  EVT_BUTTON( wxKEYBINDER_REMOVE_KEY_ID, wxKeyConfigPanel::OnRemoveKey )
  EVT_BUTTON( wxKEYBINDER_REMOVEALL_KEY_ID, wxKeyConfigPanel::OnRemoveAllKey )
  EVT_BUTTON( wxKEYBINDER_ADD_PROFILEBTN_ID, wxKeyConfigPanel::OnAddProfile )
  EVT_BUTTON( wxKEYBINDER_REMOVE_PROFILEBTN_ID, wxKeyConfigPanel::OnRemoveProfile )
END_EVENT_TABLE()

int wxCmd::m_nCmdTypes = 0;
wxCmd::wxCmdType wxCmd::m_arrCmdType[];
wxArrayString wxKeyBinder::usableWindows;
wxWindow* mainAppWindow = 0;
extern wxString* pKeyFilename;

wxString wxKeyBind::NumpadKeyCodeToString( int keyCode ) {
  wxString res;
  switch( keyCode ) {
    case WXK_NUMPAD0:
    case WXK_NUMPAD1:
    case WXK_NUMPAD2:
    case WXK_NUMPAD3:
    case WXK_NUMPAD4:
    case WXK_NUMPAD5:
    case WXK_NUMPAD6:
    case WXK_NUMPAD7:
    case WXK_NUMPAD8:
    case WXK_NUMPAD9:
      res << wxString::Format( _T( "%d" ), keyCode - WXK_NUMPAD0 );
      break;
    case WXK_NUMPAD_SPACE:
      res << wxT( "SPACE" );
      break;
    case WXK_NUMPAD_TAB:
      res << wxT( "TAB" );
      break;
    case WXK_NUMPAD_ENTER:
      res << wxT( "ENTER" );
      break;
    case WXK_NUMPAD_F1:
    case WXK_NUMPAD_F2:
    case WXK_NUMPAD_F3:
    case WXK_NUMPAD_F4:
      res << wxT( "F" ) << wxString::Format( _T( "%d" ), keyCode - WXK_NUMPAD_F1 );
      break;
    case WXK_NUMPAD_LEFT:
      res << wxT( "LEFT" );
      break;
    case WXK_NUMPAD_UP:
      res << wxT( "UP" );
      break;
    case WXK_NUMPAD_RIGHT:
      res << wxT( "RIGHT" );
      break;
    case WXK_NUMPAD_DOWN:
      res << wxT( "DOWN" );
      break;
    case WXK_NUMPAD_HOME:
      res << wxT( "HOME" );
      break;
    case WXK_NUMPAD_PAGEUP:
      res << wxT( "PAGEUP" );
      break;
    case WXK_NUMPAD_PAGEDOWN:
      res << wxT( "PAGEDOWN" );
      break;
    case WXK_NUMPAD_END:
      res << wxT( "END" );
      break;
    case WXK_NUMPAD_BEGIN:
      res << wxT( "BEGIN" );
      break;
    case WXK_NUMPAD_INSERT:
      res << wxT( "INSERT" );
      break;
    case WXK_NUMPAD_DELETE:
      res << wxT( "DELETE" );
      break;
    case WXK_NUMPAD_EQUAL:
      res << wxT( "=" );
      break;
    case WXK_NUMPAD_MULTIPLY:
      res << wxT( "*" );
      break;
    case WXK_NUMPAD_ADD:
      res << wxT( "+" );
      break;
    case WXK_NUMPAD_SEPARATOR:
      res << wxT( "SEPARATOR" );
      break;
    case WXK_NUMPAD_SUBTRACT:
      res << wxT( "-" );
      break;
    case WXK_NUMPAD_DECIMAL:
      res << wxT( "." );
      break;
    case WXK_NUMPAD_DIVIDE:
      res << wxT( "/" );
      break;
    default:
      break;
  }
  return res;
}

wxString wxKeyBind::KeyCodeToString( int keyCode ) {
  wxString res;
  switch( keyCode ) {
    case WXK_START:
    case WXK_LBUTTON:
    case WXK_RBUTTON:
    case WXK_MBUTTON:
    case WXK_CLEAR:
    case WXK_PAUSE:
    case WXK_NUMLOCK:
    case WXK_SCROLL :
      return wxEmptyString;
    case WXK_SHIFT:
    case WXK_CONTROL:
    case WXK_ALT:
      return wxEmptyString;
    case WXK_F1:
    case WXK_F2:
    case WXK_F3:
    case WXK_F4:
    case WXK_F5:
    case WXK_F6:
    case WXK_F7:
    case WXK_F8:
    case WXK_F9:
    case WXK_F10:
    case WXK_F11:
    case WXK_F12:
    case WXK_F13:
    case WXK_F14:
    case WXK_F15:
    case WXK_F16:
    case WXK_F17:
    case WXK_F18:
    case WXK_F19:
    case WXK_F20:
    case WXK_F21:
    case WXK_F22:
    case WXK_F23:
    case WXK_F24:
      res << wxT( 'F' ) << wxString::Format( _T( "%d" ), keyCode - WXK_F1 + 1 );
      break;
    case WXK_BACK:
      res << wxT( "BACK" );
      break;
    case WXK_TAB:
      res << wxT( "TAB" );
      break;
    case WXK_RETURN:
      res << wxT( "RETURN" );
      break;
    case WXK_ESCAPE:
      res << wxT( "ESCAPE" );
      break;
    case WXK_SPACE:
      res << wxT( "SPACE" );
      break;
    case WXK_DELETE:
      res << wxT( "DELETE" );
      break;
    case WXK_MULTIPLY:
      res << wxT( "*" );
      break;
    case WXK_ADD:
      res << wxT( "+" );
      break;
    case WXK_SEPARATOR:
      res << wxT( "SEPARATOR" );
      break;
    case WXK_SUBTRACT:
      res << wxT( "-" );
      break;
    case WXK_DECIMAL:
      res << wxT( "." );
      break;
    case WXK_DIVIDE:
      res << wxT( "/" );
      break;
    case WXK_PAGEUP:
      res << wxT( "PAGEUP" );
      break;
    case WXK_PAGEDOWN:
      res << wxT( "PAGEDOWN" );
      break;
    case WXK_LEFT:
      res << wxT( "LEFT" );
      break;
    case WXK_UP:
      res << wxT( "UP" );
      break;
    case WXK_RIGHT:
      res << wxT( "RIGHT" );
      break;
    case WXK_DOWN:
      res << wxT( "DOWN" );
      break;
    case WXK_SELECT:
      res << wxT( "SELECT" );
      break;
    case WXK_PRINT:
      res << wxT( "PRINT" );
      break;
    case WXK_EXECUTE:
      res << wxT( "EXECUTE" );
      break;
    case WXK_SNAPSHOT:
      res << wxT( "SNAPSHOT" );
      break;
    case WXK_INSERT:
      res << wxT( "INSERT" );
      break;
    case WXK_HELP:
      res << wxT( "HELP" );
      break;
    case WXK_CANCEL:
      res << wxT( "CANCEL" );
      break;
    case WXK_MENU:
      res << wxT( "MENU" );
      break;
    case WXK_CAPITAL:
      res << wxT( "CAPITAL" );
      break;
    case WXK_END:
      res << wxT( "END" );
      break;
    case WXK_HOME:
      res << wxT( "HOME" );
      break;
    default:
      if( wxIsalnum( keyCode ) ) {
        res << ( wxChar )keyCode;
        break;
      } else if( ( res = NumpadKeyCodeToString( keyCode ) ) != wxEmptyString ) {
        res << wxT( " (numpad)" );
        break;
      } else if( wxIsprint( keyCode ) ) {
        res << ( wxChar )keyCode;
        break;
      } else
      { return wxEmptyString; }
  }
  return res;
}
int wxKeyBind::StringToKeyCode( const wxString &keyName ) {
  if( keyName.StartsWith( wxT( "F" ) ) && keyName.Len() > 1 ) {
    long n;
    keyName.Right( keyName.Len() - 1 ).ToLong( &n );
    return WXK_F1 + n - 1;
  }
  if( keyName == wxT( "BACK" ) ) {
    return WXK_BACK;
  }
  if( keyName == wxT( "ENTER" ) ) {
    return WXK_RETURN;
  }
  if( keyName == wxT( "RETURN" ) ) {
    return WXK_RETURN;
  }
  if( keyName == wxT( "TAB" ) ) {
    return WXK_TAB;
  }
  if( keyName == wxT( "ESCAPE" ) ) {
    return WXK_ESCAPE;
  }
  if( keyName == wxT( "SPACE" ) ) {
    return WXK_SPACE;
  }
  if( keyName == wxT( "DELETE" ) ) {
    return WXK_DELETE;
  }
  if( keyName == wxT( "LEFT" ) ) {
    return WXK_LEFT ;
  }
  if( keyName == wxT( "UP" ) ) {
    return WXK_UP;
  }
  if( keyName == wxT( "RIGHT" ) ) {
    return WXK_RIGHT;
  }
  if( keyName == wxT( "DOWN" ) ) {
    return WXK_DOWN;
  }
  if( keyName == wxT( "HOME" ) ) {
    return WXK_HOME;
  }
  if( keyName == wxT( "PAGEUP" ) ) {
    return WXK_PAGEUP;
  }
  if( keyName == wxT( "PAGEDOWN" ) ) {
    return WXK_PAGEDOWN;
  }
  if( keyName == wxT( "END" ) ) {
    return WXK_END;
  }
  if( keyName == wxT( "INSERT" ) ) {
    return WXK_INSERT;
  }
  if( keyName == wxT( "DELETE" ) ) {
    return WXK_DELETE;
  }
  if( keyName == wxT( "LEFT (numpad)" ) ) {
    return WXK_NUMPAD_LEFT ;
  }
  if( keyName == wxT( "UP (numpad)" ) ) {
    return WXK_NUMPAD_UP;
  }
  if( keyName == wxT( "RIGHT (numpad)" ) ) {
    return WXK_NUMPAD_RIGHT;
  }
  if( keyName == wxT( "DOWN (numpad)" ) ) {
    return WXK_NUMPAD_DOWN;
  }
  if( keyName == wxT( "HOME (numpad)" ) ) {
    return WXK_NUMPAD_HOME;
  }
  if( keyName == wxT( "PAGEUP (numpad)" ) ) {
    return WXK_NUMPAD_PAGEUP;
  }
  if( keyName == wxT( "PAGEDOWN (numpad)" ) ) {
    return WXK_NUMPAD_PAGEDOWN;
  }
  if( keyName == wxT( "END (numpad)" ) ) {
    return WXK_NUMPAD_END;
  }
  if( keyName == wxT( "BEGIN (numpad)" ) ) {
    return WXK_NUMPAD_BEGIN;
  }
  if( keyName == wxT( "INSERT (numpad)" ) ) {
    return WXK_NUMPAD_INSERT;
  }
  if( keyName == wxT( "DELETE (numpad)" ) ) {
    return WXK_NUMPAD_DELETE;
  }
  if( keyName == wxT( "= (numpad)" ) ) {
    return WXK_NUMPAD_EQUAL;
  }
  if( keyName == wxT( "* (numpad)" ) ) {
    return WXK_NUMPAD_MULTIPLY;
  }
  if( keyName == wxT( "+ (numpad)" ) ) {
    return WXK_NUMPAD_ADD;
  }
  if( keyName == wxT( ". (numpad)" ) ) {
    return WXK_NUMPAD_DECIMAL;
  }
  if( keyName == wxT( "/ (numpad)" ) ) {
    return WXK_NUMPAD_DIVIDE;
  }
  return ( int )keyName.GetChar( 0 );
}


wxString wxKeyBind::KeyModifierToString( int keyModifier ) {
  wxString result;
  if( keyModifier & wxACCEL_CMD ) {
    result += wxT( "Ctrl-" );
  }
  if( keyModifier & wxACCEL_ALT ) {
    result += wxT( "Alt-" );
  }
  if( keyModifier & wxACCEL_SHIFT ) {
    result += wxT( "Shift-" );
  }
  return result;
}


int wxKeyBind::StringToKeyModifier( const wxString &keyModifier ) {
  int mod = 0;
  wxString str = keyModifier;
  str.MakeUpper();
  if( str.Contains( wxT( "ALT" ) ) ) {
    mod |= wxACCEL_ALT;
  }
  if( str.Contains( wxT( "CTRL" ) ) ) {
    mod |= wxACCEL_CMD;
  }
  if( str.Contains( wxT( "SHIFT" ) ) ) {
    mod |= wxACCEL_SHIFT;
  }
  return mod;
}


int wxKeyBind::GetKeyModifier( wxKeyEvent &event ) {
  int mod = 0;
  if( event.AltDown() ) {
    mod |= wxACCEL_ALT;
  }
  if( event.ControlDown() ) {
    mod |= wxACCEL_CTRL;
  }
  if( event.ShiftDown() ) {
    mod |= wxACCEL_SHIFT;
  }
  return mod;
}

wxString wxKeyBind::GetKeyStrokeString( wxKeyEvent &event ) {
  return KeyModifierToString( GetKeyModifier( event ) ) +
         KeyCodeToString( event.GetKeyCode() );
}

bool wxKeyBind::MatchKey( const wxKeyEvent &key ) const {
  bool b = ( key.GetKeyCode() == m_nKeyCode );
  b &= ( key.AltDown() == ( ( m_nFlags & wxACCEL_ALT ) != 0 ) );
  b &= ( key.ControlDown() == ( ( m_nFlags & wxACCEL_CTRL ) != 0 ) );
  b &= ( key.ShiftDown() == ( ( m_nFlags & wxACCEL_SHIFT ) != 0 ) );
  return b;
}

wxCmd::wxCmdType *wxCmd::FindCmdType( int type ) {
  int found = -1;
  for( int i = 0; i < m_nCmdTypes; i++ )
    if( m_arrCmdType[i].type == type ) {
      found = i;
    }
  if( found == -1 ) {
    return NULL;
  }
  return &m_arrCmdType[found];
}

wxCmd *wxCmd::CreateNew( wxString cmdName, int type, int id, bool updateMnu ) {
  wxCmdType *found = FindCmdType( type );
  if( !found ) {
    return NULL;
  }
  wxCmdCreationFnc fnc = found->cmdCreateFnc;
  wxASSERT( fnc );
  if( !fnc ) {
    return NULL;
  }
  wxCmd* ret = fnc( cmdName, id );
  if( !ret ) {
    return NULL;
  }
  if( updateMnu ) {
    ret->Update();
  }
  return ret;
}

void wxCmd::AddCmdType( int type, wxCmdCreationFnc f ) {
  if( FindCmdType( type ) != NULL ) {
    return;
  }
  m_arrCmdType[m_nCmdTypes].type = type;
  m_arrCmdType[m_nCmdTypes].cmdCreateFnc = f;
  m_nCmdTypes++;
}

bool wxCmd::Save( wxConfigBase *p, const wxString &key, bool bCleanOld ) const {
  wxString shortcuts;
  for( int j = 0; j < GetShortcutCount(); ++j ) {
    shortcuts += GetShortcut( j )->GetStr() + wxT( "|" );
  }
  const wxString fullMenuPath = GetFullMenuPath( GetId() );
  wxString value = wxString::Format( wxT( "%s|%s|%s" ), fullMenuPath.c_str(), GetDescription().c_str(), shortcuts.c_str() );
  if( bCleanOld && p->Exists( key ) ) {
    p->DeleteEntry( key );
  }
  return p->Write( key, value );
}

bool wxCmd::Load( wxConfigBase *p, const wxString &key ) {
  wxString fmt;
  if( !p->Read( key, &fmt, wxT( "|" ) ) ) {
    return FALSE;
  }
  wxStringTokenizer tknzr( fmt, wxT( "|" ) );
  m_strName = tknzr.GetNextToken();
  m_strDescription = tknzr.GetNextToken();
  if( m_strName.IsEmpty() ) {
    return FALSE;
  }
  wxString fullMenuPath = m_strName;
  m_strName = fullMenuPath.AfterLast( wxT( '\\' ) );
  wxASSERT_MSG( m_nId != wxID_INVALID, wxT( "ID must be set while creating of this wxCmd" ) );
  while( tknzr.HasMoreTokens() ) {
    wxString token = tknzr.GetNextToken();
    AddShortcut( token );
  }
  Update();
  return TRUE;
}

bool wxCmd::LoadFromString( const wxString& cfgCmdString ) {
  wxString fmt = cfgCmdString;;
  if( fmt.empty() ) {
    return FALSE;
  }
  wxStringTokenizer tknzr( fmt, wxT( "|" ) );
  m_strName = tknzr.GetNextToken();
  m_strDescription = tknzr.GetNextToken();
  if( m_strName.IsEmpty() ) {
    return FALSE;
  }
  wxString fullMenuPath = m_strName;
  m_strName = fullMenuPath.AfterLast( wxT( '\\' ) );
  wxASSERT_MSG( m_nId != wxID_INVALID, wxT( "ID must be set while creating of this wxCmd" ) );
  while( tknzr.HasMoreTokens() ) {
    wxString token = tknzr.GetNextToken();
    AddShortcut( token );
  }
  Update();
  return TRUE;
}

void wxCmdArray::Remove( int n ) {
  if( n < 0 || n >= GetCount() ) {
    return;
  }
  delete Item( n );
  m_arr.RemoveAt( n );
}

void wxCmdArray::Clear() {
  for( int i = GetCount(); i > 0; i-- ) {
    Remove( 0 );
  }
  m_arr.Clear();
}

void wxBinderEvtHandler::OnChar( wxKeyEvent &p ) {
  m_pBinder->OnChar( p, GetNextHandler() );
}

int wxBinderApp::FilterEvent( wxEvent &ev ) {
  wxEvtHandler *client = m_pGlobalHdl;
  wxEvtHandler *top = GetTopWindow();
  wxWindow *focused = wxWindow::FindFocus();
  if( !client ) {
    client = top;
  }
  if( client != top ) {
    return -1;
  }
  wxASSERT( client );
  wxEventType t = ev.GetEventType();
  if( t == wxEVT_KEY_DOWN ) {
    if( focused != NULL && focused != client &&
        GetTopLevelParent( focused ) != client ) {
      return -1;
    }
    m_pGlobalBinder->OnChar( ( wxKeyEvent & )ev, client );
    return ev.GetSkipped();
  }
  return -1;
}

wxWindow *wxBinderApp::GetTopLevelParent( wxWindow *wnd ) {
  if( wnd->IsTopLevel() ) {
    return wnd;
  }
  if( wnd->GetParent() ) {
    return GetTopLevelParent( wnd->GetParent() );
  }
  return FALSE;
}

bool wxBinderApp::IsChildOf( wxWindow *parent, wxWindow *child ) {
  if( parent == child ) {
    return TRUE;
  }
  for( wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst(); node; node = node->GetNext() ) {
    wxWindow *win = ( wxWindow * )node->GetData();
    if( win && IsChildOf( win, child ) ) {
      return TRUE;
    }
  }
  return FALSE;
}

void wxKeyBinder::GetMenuItemAccStr( wxMenuItem* pMenuItem, wxString& MenuItemKeyStr ) {
  MenuItemKeyStr = wxEmptyString;
  wxAcceleratorEntry* pAcc = pMenuItem->GetAccel();
  if( pAcc ) {
    MenuItemKeyStr = wxKeyBind::KeyModifierToString( pAcc->GetFlags() ) + wxKeyBind::KeyCodeToString( pAcc->GetKeyCode() );
  }
  if( pAcc ) {
    delete pAcc;
  }
}

int wxKeyBinder::MergeSubMenu( wxMenu* pMenu, int& modified ) {
  int changed = 0;
  size_t itemKnt = pMenu->GetMenuItemCount();
  for( size_t j = 0; j < itemKnt; j++ ) {
    wxMenuItem* pMenuItem = pMenu->FindItemByPosition( j );
    if( pMenuItem->GetSubMenu() ) {
      MergeSubMenu( pMenuItem->GetSubMenu(), modified );
    }
    if( pMenuItem->GetKind() == wxITEM_SEPARATOR ) {
      continue;
    }
    int nMenuItemID = pMenuItem->GetId();
    if( wxMenuCmd::IsNumericMenuItem( pMenuItem ) ) {
      continue;
    }
    wxCmd*  pCmd = 0;
    changed = 0;
    wxString menuItemLabel = pMenuItem->GetLabel().Trim();
    wxString menuItemKeyStr;
    GetMenuItemAccStr( pMenuItem, menuItemKeyStr );
    if( ( pCmd = GetCmd( nMenuItemID ) ) ) {
      if( pCmd->GetName().Trim() != menuItemLabel.Trim() ) {
        changed = 1;
      }
      if( menuItemKeyStr.IsEmpty() != ( pCmd->GetShortcutCount() == 0 ) ) {
        changed = 2;
      }
      if( pCmd->GetShortcutCount() && ( GetShortcutStr( nMenuItemID, 0 ) != menuItemKeyStr ) ) {
        changed = 3;
      }
    } else
    { changed = 4; }
    if( changed ) {
      wxCmd* p = 0;
      while( ( p = GetCmdBindTo( menuItemKeyStr ) ) ) {
        wxKeyBind tmp( menuItemKeyStr );
        int n;
        if( p->IsBindTo( tmp, &n ) ) {
          p->RemoveShortcut( n, true );
        }
      }
      if( changed < 4 ) {
        RemoveCmd( pCmd );
      }
      if( ( 6 == menuItemKeyStr.Length() ) && ( menuItemKeyStr.StartsWith( _T( "Ctrl-" ) ) ) ) {
        const wxChar c = menuItemKeyStr.GetChar( 5 );
        switch( c ) {
          case _T( 'C' ):
            if( menuItemLabel.Matches( _T( "Copy" ) ) )
            { continue; }
          case _T( 'V' ):
            if( menuItemLabel.Matches( _T( "Paste" ) ) )
            { continue; }
          case _T( 'S' ):
            if( menuItemLabel.Matches( _T( "Cut" ) ) )
            { continue; }
          default:
            break;
        }
        if( ( c == _T( 'C' ) ) || ( c == _T( 'V' ) ) || ( c == _T( 'X' ) ) ) {
          continue;
        }
      }
      pCmd = wxCmd::CreateNew( menuItemLabel, wxMENUCMD_TYPE, nMenuItemID, false );
      if( not pCmd ) {
        return modified;
      }
      pCmd->m_strName = menuItemLabel;
      pCmd->m_strDescription = pMenuItem->GetHelp();
      AddCmd( pCmd );
      AddShortcut( nMenuItemID, menuItemKeyStr, true );
    }
    modified += ( changed != 0 ) ;
  }
  return modified;
}

int wxKeyBinder::MergeDynamicMenuItems( wxMenuBar* pMenuBar ) {
  int changed = 0;
  size_t nLevel1Knt = pMenuBar->GetMenuCount();
  for( size_t i = 0; i < nLevel1Knt ; i++ ) {
    wxMenu* pMenu = pMenuBar->GetMenu( i );
    MergeSubMenu( pMenu, changed );
  }
  wxCmdArray* pCmdArray = GetArray();
  for( int i = 0; i < GetCmdCount(); i++ ) {
    wxCmd* pCmd = pCmdArray->Item( i );
    if( not pMenuBar->FindItem( pCmd->GetId(), NULL ) ) {
      RemoveCmd( pCmd );
      ++changed;
    }
  }
  return ( changed );
}

void wxKeyBinder::UpdateSubMenu( wxMenu* pMenu ) {
  size_t itemKnt = pMenu->GetMenuItemCount();
  for( size_t j = 0; j < itemKnt; j++ ) {
    wxMenuItem* pMenuItem = pMenu->FindItemByPosition( j );
    if( pMenuItem->GetSubMenu() ) {
      UpdateSubMenu( pMenuItem->GetSubMenu() );
    }
    int nMenuItemID = pMenuItem->GetId();
    int k = 0;
    wxString menuItemKeyStr;
    if( -1 != ( k = FindCmd( nMenuItemID ) ) ) {
      GetMenuItemAccStr( pMenuItem, menuItemKeyStr );
      m_arrCmd.Item( k )->Update( pMenuItem );
    }
  }
}

void wxKeyBinder::UpdateAllCmd( wxMenuBar* pMenuBar ) {
  if( m_arrHandlers.GetCount() == 0 ) {
    return;
  }
  size_t nLevel1Knt = pMenuBar->GetMenuCount();
  for( size_t i = 0; i < nLevel1Knt ; i++ ) {
    wxMenu* pMenu = pMenuBar->GetMenu( i );
    UpdateSubMenu( pMenu );
  }
}

wxBinderEvtHandler *wxKeyBinder::FindHandlerFor( wxWindow *p ) const {
  int idx = FindHandlerIdxFor( p );
  if( idx == wxNOT_FOUND ) {
    return NULL;
  }
  return ( wxBinderEvtHandler * )m_arrHandlers.Item( idx );
}

int wxKeyBinder::FindHandlerIdxFor( wxWindow *p ) const {
  for( int i = 0; i < ( int )m_arrHandlers.GetCount(); i++ )
    if( ( ( wxBinderEvtHandler * )m_arrHandlers.Item( i ) )->IsAttachedTo( p ) ) {
      return i;
    }
  return wxNOT_FOUND;
}

void wxKeyBinder::Attach( wxWindow *p ) {
  if( !p || IsAttachedTo( p ) ) {
    return;
  }
  if( p->GetExtraStyle() & wxWS_EX_TRANSIENT ) {
    return;
  }
  wxString windowName = p->GetName().MakeLower();
  if( wxNOT_FOUND == usableWindows.Index( _T( "*" ), false ) )
    if( wxNOT_FOUND == usableWindows.Index( windowName, false ) ) {
      return;
    }
  wxEvtHandler *h = new wxBinderEvtHandler( this, p );
  m_arrHandlers.Add( ( void* )h );
}

void wxKeyBinder::AttachRecursively( wxWindow *p ) {
  if( !p ) {
    return;
  }
  if( NOT mainAppWindow ) {
    mainAppWindow = p ;
  }
  Attach( p );
  for( wxWindowList::compatibility_iterator node = p->GetChildren().GetFirst(); node; node = node->GetNext() ) {
    wxWindow *win = ( wxWindow * )node->GetData();
    if( win ) {
      AttachRecursively( win );
    }
  }
}

wxWindow* wxKeyBinder::FindWindowRecursively( const wxWindow* parent, const wxWindow* handle ) {
  if( parent ) {
    if( parent == handle ) {
      return ( wxWindow * )parent;
    }
    for( wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst(); node; node = node->GetNext() ) {
      wxWindow *win = ( wxWindow * )node->GetData();
      wxWindow *retwin = FindWindowRecursively( win, handle );
      if( retwin ) {
        return retwin;
      }
    }
  }
  return NULL;
}

wxWindow* wxKeyBinder::winExists( wxWindow *parent ) {
  if( !parent ) {
    return NULL;
  }
  for( wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() ) {
    wxWindow* win = node->GetData();
    wxWindow* retwin = FindWindowRecursively( win, parent );
    if( retwin ) {
      return retwin;
    }
  }
  return NULL;
}

void wxKeyBinder::OnWinClosed( wxCloseEvent& event ) {
  wxWindow* pwin = ( wxWindow* )event.GetEventObject();
  Detach( pwin );
  event.Skip();
  return;
}

void wxKeyBinder::Detach( wxWindow *p, bool deleteEvtHandler ) {
  if( !p || !IsAttachedTo( p ) ) {
    return;
  }
  int idx = FindHandlerIdxFor( p );
  wxBinderEvtHandler *toremove = ( wxBinderEvtHandler* )m_arrHandlers.Item( idx );
  m_arrHandlers.RemoveAt( idx, 1 );
  if( deleteEvtHandler ) {
    delete toremove;
  }
}

void wxKeyBinder::DetachAll() {
  wxWindow* pwin;
  for( int i = 0; i < ( int )m_arrHandlers.GetCount(); i++ ) {
    wxBinderEvtHandler* pHdlr = ( wxBinderEvtHandler* )m_arrHandlers.Item( i );
    pwin = pHdlr->GetTargetWnd();
    if( NOT winExists( pwin ) ) {
      pHdlr->SetWndInvalid( 0 );
    }
    delete pHdlr;
  }
  m_arrHandlers.Clear();
}

void wxKeyBinder::ImportMenuBarCmd( wxMenuBar *p ) {
  wxMenuShortcutWalker wlkr;
  wlkr.ImportMenuBarCmd( p, &m_arrCmd );
}

void wxKeyBinder::OnChar( wxKeyEvent &event, wxEvtHandler *next ) {
  wxCmd *p = GetMatchingCmd( event );
  if( p && p->IsBindTo( wxKeyBind( wxT( "Alt+F4" ) ) ) ) {
    event.Skip();
    return;
  }
  if( p == NULL ) {
    event.Skip();
  } else {
    wxEvtHandler *client = next;
    if( client == NULL ) {
      event.Skip();
      return;
    }
    p->Exec( event.GetEventObject(), client );
  }
}

bool wxKeyBinder::Save( wxConfigBase *cfg, const wxString &key, bool bCleanOld ) const {
  wxString basekey = ( key.IsEmpty() ? wxString( wxT( "" ) ) : wxString( key + wxT( "/" ) ) );
  bool b = TRUE;
  if( bCleanOld && cfg->Exists( basekey ) ) {
    cfg->DeleteGroup( basekey );
  }
  for( int i = 0; i < m_arrCmd.GetCount(); i++ ) {
    wxCmd *curr = m_arrCmd.Item( i );
    wxString keyname = wxString::Format( wxT( "%s%s%d-type%d" ),
                                         basekey.wx_str(), wxCMD_CONFIG_PREFIX, curr->GetId(), curr->GetType() );
    b &= curr->Save( cfg, keyname );
  }
  return b;
}

bool wxKeyBinder::GetNameandDescription( wxConfigBase* p, const wxString& key, wxString& strName, wxString& strDescription ) {
  wxString fmt;
  if( !p->Read( key, &fmt, wxT( "|" ) ) ) {
    return FALSE;
  }
  wxStringTokenizer tknzr( fmt, wxT( "|" ) );
  strName = tknzr.GetNextToken();
  strDescription = tknzr.GetNextToken();
  if( strName.IsEmpty() ) {
    return FALSE;
  }
  return TRUE;
}

bool wxKeyBinder::Load( wxConfigBase *p, const wxString &key ) {
  wxString str;
  bool cont;
  bool b = TRUE;
  int total = 0;
  long idx;
  p->SetPath( key );
  m_arrCmd.Clear();
  cont = p->GetFirstEntry( str, idx );
  while( cont ) {
    if( str.StartsWith( wxCMD_CONFIG_PREFIX ) ) {
      wxString id( str.BeforeFirst( wxT( '-' ) ) );
      wxString type( str.AfterFirst( wxT( '-' ) ) );
      id = id.Right( id.Len() - wxString( wxCMD_CONFIG_PREFIX ).Len() );
      type = type.Right( type.Len() - wxString( wxT( "type" ) ).Len() );
      if( str.StartsWith( _T( "bind-" ) ) ) {
        id = _T( "-" ) + str.Mid( 5 ).BeforeFirst( _T( '-' ) );
        int typepos = str.Find( _T( "type" ) );
        if( typepos != wxNOT_FOUND ) {
          type = str.Mid( typepos + 4 ).BeforeFirst( _T( '=' ) );
        }
      }
      if( id.IsNumber() && type.IsNumber()
        ) {
        int nid = wxAtoi( id );
        int ntype = wxAtoi( type );
        wxString cmdName;
        wxString cmdDesc;
        if( not GetNameandDescription( p, str, cmdName, cmdDesc ) ) {
        }
        wxCmd* cmd = wxCmd::CreateNew( cmdName, ntype, nid );
        if( cmd && cmd->Load( p, str ) ) {
          m_arrCmd.Add( cmd );
          total++;
        }
      }
    }
    cont &= p->GetNextEntry( str, idx );
  }
  return ( b && total > 0 );
}

bool wxKeyBinder::LoadFromString( const wxString& cfgCmdString ) {
  wxString str = cfgCmdString;
  bool b = TRUE;
  int total = 0;
  for( int once = 0; once == 0 ; ++once ) {
    if( str.StartsWith( wxCMD_CONFIG_PREFIX ) ) {
      wxString id( str.BeforeFirst( wxT( '-' ) ) );
      wxString type( str.AfterFirst( wxT( '-' ) ) );
      type = type.BeforeFirst( _T( '=' ) );
      type = type.Mid( 4 );
      id = id.Right( id.Len() - wxString( wxCMD_CONFIG_PREFIX ).Len() );
      if( id.IsNumber() && type.IsNumber() ) {
        int nid = wxAtoi( id );
        int ntype = wxAtoi( type );
        wxString cmdName;
        wxString cmdDesc;
        cmdDesc = str.AfterFirst( _T( '|' ) );
        cmdDesc = cmdDesc.BeforeFirst( _T( '|' ) );
        cmdName = str.After( _T( '\\' ) );
        cmdName = cmdName.BeforeFirst( _T( '|' ) );
        wxCmd* cmd = wxCmd::CreateNew( cmdName, ntype, nid );
        if( cmd && cmd->LoadFromString( str ) ) {
          m_arrCmd.Add( cmd );
          total++;
        }
      }
    }
  }
  return ( b && total > 0 );
}

bool wxKeyProfile::Save( wxConfigBase *cfg, const wxString &key, bool bCleanOld ) const {
  wxString basekey = key.IsEmpty() ? wxString( wxT( "" ) ) : wxString( key + wxT( "/" ) );
  if( bCleanOld && cfg->Exists( basekey ) ) {
    cfg->DeleteGroup( basekey );
  }
  if( !cfg->Write( basekey + wxT( "/desc" ), GetDesc() ) ) {
    return FALSE;
  }
  if( !cfg->Write( basekey + wxT( "/name" ), GetName() ) ) {
    return FALSE;
  }
  return wxKeyBinder::Save( cfg, basekey, FALSE );
}

bool wxKeyProfile::Load( wxConfigBase *p, const wxString &key ) {
  p->SetPath( key );
  wxString name;
  wxString desc;
  if( p->HasEntry( wxT( "desc" ) ) && p->HasEntry( wxT( "name" ) ) ) {
    if( !p->Read( wxT( "desc" ), &desc ) ) {
      return FALSE;
    }
    if( !p->Read( wxT( "name" ), &name ) ) {
      return FALSE;
    }
    if( name.IsEmpty() ) {
      return FALSE;
    }
    SetName( name );
    SetDesc( desc );
    return wxKeyBinder::Load( p, wxT( "../" ) + key );
  }
  return FALSE;
}

bool wxKeyProfileArray::Save( wxConfigBase *cfg, const wxString &key, bool bCleanOld ) const {
  wxString basekey = ( key.IsEmpty() ) ? wxString( wxT( "" ) ) : wxString( key + wxT( "/" ) );
  bool b = TRUE;
  cfg->SetPath( key );
  if( !cfg->Write( basekey + wxT( "nSelProfile" ), m_nSelected ) ) {
    return FALSE;
  }
  for( int i = 0; i < GetCount(); i++ ) {
    b &= Item( i )->Save( cfg, basekey + wxKEYPROFILE_CONFIG_PREFIX + wxString::Format( wxT( "%d" ), i ), bCleanOld );
  }
  if( bCleanOld ) {
    cfg->SetPath( key );
    wxString str;
    long n, idx;
    bool cont = cfg->GetFirstGroup( str, idx );
    while( cont ) {
      if( str.StartsWith( wxKEYPROFILE_CONFIG_PREFIX ) ) {
        wxString id = str.Right( str.Len() - wxString( wxKEYPROFILE_CONFIG_PREFIX ).Len() );
        id.ToLong( &n );
        if( n >= GetCount() ) {
          cfg->DeleteGroup( str );
          cont = cfg->GetFirstGroup( str, idx );
          if( !cont ) {
            break;
          }
        }
      }
      cont &= cfg->GetNextGroup( str, idx );
    }
  }
  return b;
}

bool wxKeyProfileArray::Load( wxConfigBase *p, const wxString &key ) {
  wxKeyProfile tmp;
  wxString str;
  bool cont;
  long idx;
  p->SetPath( key );
  if( !p->Read( wxT( "nSelProfile" ), &m_nSelected ) ) {
    return FALSE;
  }
  cont = p->GetFirstGroup( str, idx );
  while( cont ) {
    if( str.StartsWith( wxKEYPROFILE_CONFIG_PREFIX ) ) {
      if( !tmp.Load( p, str ) ) {
        return FALSE;
      }
      Add( new wxKeyProfile( tmp ) );
    }
    p->SetPath( key );
    cont &= p->GetNextGroup( str, idx );
  }
  return TRUE;
}

void wxKeyMonitorTextCtrl::OnKey( wxKeyEvent &event ) {
  if( event.GetKeyCode() == WXK_BACK ) {
    Clear();
    return;
  }
  if( event.GetEventType() == wxEVT_KEY_DOWN || ( event.GetEventType() == wxEVT_KEY_UP && !IsValidKeyComb() ) ) {
    wxString keyStrokeString = wxKeyBind::GetKeyStrokeString( event );
    if( not keyStrokeString.IsEmpty() )
      do {
        if( keyStrokeString.Length() < 2 ) {
          keyStrokeString.Clear();
          break;
        }
        if( ( keyStrokeString[0] == 'F' ) && ( keyStrokeString.Mid( 1, 1 ).IsNumber() ) ) {
          break;
        }
        if( not( validCmdPrefixes.Contains( keyStrokeString.BeforeFirst( '-' ) ) ) ) {
          keyStrokeString.Clear();
          break;
        }
      } while( 0 );
    SetValue( keyStrokeString );
    SetInsertionPointEnd();
  }
}

wxKeyConfigPanel::wxKeyConfigPanel( wxWindow* parent, int buildMode, wxWindowID id, const wxPoint& pos, const wxSize& size,
                                    long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name ) {
  m_nBuildMode = buildMode;
  m_bProfileHasBeenModified = FALSE;
  wxASSERT_MSG( ( m_nBuildMode & wxKEYBINDER_USE_LISTBOX ) || ( m_nBuildMode & wxKEYBINDER_USE_TREECTRL ),
                wxT( "You must specify one of the two !!" ) );
  wxASSERT_MSG( !( ( m_nBuildMode & wxKEYBINDER_USE_LISTBOX ) && ( m_nBuildMode & wxKEYBINDER_USE_TREECTRL ) ),
                wxT( "You cannot specify them both !!" ) );
  BuildCtrls();
  wxSizer *column1 = BuildColumn1();
  wxSizer *column2 = BuildColumn2();
  wxSizer *main = BuildMain( column1, column2, ( m_nBuildMode & wxKEYBINDER_SHOW_APPLYBUTTON ) != 0 );
  SetSizer( main );
  main->SetSizeHints( this );
  GetMainCtrl()->SetFocus();
  UpdateButtons();
}

wxKeyConfigPanel::~wxKeyConfigPanel() {
  for( size_t i = 0; i < m_pKeyProfiles->GetCount(); i++ ) {
    wxKeyProfile *data = ( wxKeyProfile * )m_pKeyProfiles->GetClientData( i );
    if( data ) {
      delete data;
    }
  }
}

void wxKeyConfigPanel::BuildCtrls() {
  if( m_nBuildMode & wxKEYBINDER_USE_TREECTRL ) {
    m_pCommandsTree = new wxTreeCtrl( this, wxKEYBINDER_COMMANDS_BOX_ID, wxDefaultPosition,
                                      wxDefaultSize, wxTR_HAS_BUTTONS | wxSUNKEN_BORDER );
  } else {
    m_pCommandsList = new wxListBox( this, wxKEYBINDER_COMMANDS_BOX_ID, wxDefaultPosition, wxDefaultSize );
    m_pCategories = new wxComboBox( this, wxKEYBINDER_CATEGORIES_ID, wxEmptyString, wxDefaultPosition, wxDefaultSize,
                                    0, NULL, wxCB_READONLY );
  }
  m_pKeyField = new wxKeyMonitorTextCtrl( this, wxKEYBINDER_KEY_FIELD_ID );
  m_pBindings = new wxListBox( this, wxKEYBINDER_BINDINGS_BOX_ID );
  m_pAssignBtn = new wxButton( this, wxKEYBINDER_ASSIGN_KEY_ID, wxT( "&Add" ) );
  m_pRemoveBtn = new wxButton( this, wxKEYBINDER_REMOVE_KEY_ID, wxT( "&Remove" ) );
  m_pRemoveAllBtn = new wxButton( this, wxKEYBINDER_REMOVEALL_KEY_ID, wxT( "Remove all" ) );
  m_pCurrCmdField = new wxStaticText( this, -1, wxT( "" ), wxDefaultPosition,
                                      wxSize( -1, 20 ), wxSIMPLE_BORDER | wxST_NO_AUTORESIZE | wxALIGN_CENTRE );
  m_pCurrCmdField->SetBackgroundColour( wxColour( 200, 200, 200 ) );
  m_pDescLabel = new wxTextCtrl( this, -1, wxT( "" ), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE );
  m_bEnableKeyProfiles = TRUE;
  long style = ( m_nBuildMode & wxKEYBINDER_ENABLE_PROFILE_EDITING ) ? 0 : wxCB_READONLY;
  m_pKeyProfiles = new wxComboBox( this, wxKEYBINDER_KEYPROFILES_ID, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, style );
  wxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
  sizer->Add( m_pKeyProfiles, 6, wxGROW );
  if( m_nBuildMode & wxKEYBINDER_SHOW_ADDREMOVE_PROFILE ) {
    sizer->Add( new wxButton( this, wxKEYBINDER_ADD_PROFILEBTN_ID, wxT( "Add new" ) ), 0,
                wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
    sizer->Add( new wxButton( this, wxKEYBINDER_REMOVE_PROFILEBTN_ID, wxT( "Remove" ) ), 0,
                wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
  }
  m_pKeyProfilesSizer = new wxBoxSizer( wxVERTICAL );
  m_pKeyProfilesSizer->Add( new wxStaticText( this, -1, wxT( "Key profile:" ) ), 0, wxGROW | wxALL, 5 );
  m_pKeyProfilesSizer->Add( sizer, 0, wxGROW | wxLEFT | wxRIGHT, 5 );
  m_pKeyProfilesSizer->Add( new wxStaticLine( this, -1 ), 0, wxGROW | wxALL, 5 );
}

wxSizer *wxKeyConfigPanel::BuildColumn1() {
  wxBoxSizer *column1 = new wxBoxSizer( wxVERTICAL );
  if( m_nBuildMode & wxKEYBINDER_USE_TREECTRL ) {
    column1->Add( new wxStaticText( this, -1, wxT( "Commands:" ) ), 0, wxGROW | wxALL, 5 );
    column1->Add( m_pCommandsTree, 1, wxGROW | wxRIGHT | wxLEFT | wxBOTTOM, 5 );
  } else {
    column1->Add( new wxStaticText( this, -1, wxT( "Categories:" ) ), 0, wxGROW | wxALL, 5 );
    column1->Add( m_pCategories, 1, wxGROW | wxRIGHT | wxLEFT | wxBOTTOM, 5 );
    column1->Add( new wxStaticText( this, -1, wxT( "Commands:" ) ), 0, wxGROW | wxRIGHT | wxLEFT | wxBOTTOM, 5 );
    column1->Add( m_pCommandsList, 5, wxGROW | wxRIGHT | wxLEFT | wxBOTTOM, 5 );
  }
  return column1;
}

wxSizer *wxKeyConfigPanel::BuildColumn2() {
  wxBoxSizer *column2 = new wxBoxSizer( wxVERTICAL );
  column2->Add( new wxStaticText( this, -1, wxT( "Current shortcuts:" ) ), 0, wxGROW | wxALL, 5 );
  column2->Add( m_pBindings, 2, wxGROW | wxRIGHT | wxLEFT, 5 );
  wxBoxSizer *removebtns = new wxBoxSizer( wxHORIZONTAL );
  removebtns->Add( m_pRemoveBtn, 1, wxGROW | wxALL, 5 );
  removebtns->Add( m_pRemoveAllBtn, 1, wxGROW | wxALL, 5 );
  column2->Add( removebtns, 0, wxGROW );
  column2->Add( new wxStaticText( this, -1, wxT( "New shortcut:" ) ), 0, wxGROW | wxALL, 5 );
  column2->Add( m_pKeyField, 0, wxGROW | wxLEFT | wxRIGHT, 5 );
  column2->Add( new wxStaticText( this, -1, wxT( "Currently assigned to:" ) ), 0, wxGROW | wxALL, 5 );
  column2->Add( m_pCurrCmdField, 0, wxGROW | wxLEFT | wxRIGHT, 5 );
  column2->Add( m_pAssignBtn, 0, wxGROW | wxALL, 5 );
  return column2;
}

wxSizer *wxKeyConfigPanel::BuildMain( wxSizer *column1, wxSizer *column2, bool bApplyBtn ) {
  wxBoxSizer *cont = new wxBoxSizer( wxHORIZONTAL );
  cont->Add( column1, 4, wxGROW );
  cont->Add( 1, 1, 0, wxGROW );
  cont->Add( column2, 4, wxGROW );
  wxBoxSizer *main = new wxBoxSizer( wxVERTICAL );
  main->Add( m_pKeyProfilesSizer, 0, wxGROW );
  main->Add( cont, 5, wxGROW );
  main->Add( new wxStaticLine( this, -1 ), 0, wxGROW | wxALL, 5 );
  main->Add( new wxStaticText( this, -1, wxT( "Description:" ) ), 0, wxGROW | wxALL, 5 );
  main->Add( m_pDescLabel, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
  if( bApplyBtn ) {
    wxBoxSizer *btn = new wxBoxSizer( wxHORIZONTAL );
    wxButton *applyBtn = new wxButton( this, wxID_APPLY,  wxT( "A&pply" ) );
    wxButton *cancelBtn = new wxButton( this, wxID_CANCEL, wxT( "C&ancel" ) );
    btn->Add( 1, 1, 1, wxGROW );
    btn->Add( applyBtn, 4, wxGROW | wxALL, 5 );
    btn->Add( 1, 1, 1, wxGROW );
    btn->Add( cancelBtn, 4, wxGROW | wxALL, 5 );
    btn->Add( 1, 1, 1, wxGROW );
    main->Add( 1, 1, 0, wxGROW );
    main->Add( btn, 1, wxGROW | wxALL, 5 );
  }
  return main;
}

void wxKeyConfigPanel::ImportMenuBarCmd( wxMenuBar *p, const wxString &rootname ) {
  Reset();
  if( IsUsingTreeCtrl() ) {
    AddRootIfMissing( rootname );
    wxMenuTreeWalker wlkr;
    wlkr.FillTreeCtrl( p, m_pCommandsTree, rootname );
    m_pCommandsTree->Expand( m_pCommandsTree->GetRootItem() );
  } else {
    wxMenuComboListWalker wlkr;
    wlkr.FillComboListCtrl( p, m_pCategories );
    m_pCategories->SetSelection( 0 );
    wxCommandEvent fake;
    OnCategorySelected( fake );
  }
}

void wxKeyConfigPanel::ImportKeyProfileCmd( const wxKeyProfile &toimport, const wxString &rootname ) {
  Reset();
  if( IsUsingTreeCtrl() ) {
    AddRootIfMissing( rootname );
    wxTreeItemId rootid = m_pCommandsTree->GetRootItem();
    const wxCmdArray *arr = toimport.GetArray();
    for( int i = 0; i < ( int )arr->GetCount(); i++ ) {
      wxExTreeItemData *treedata = new wxExTreeItemData( arr->Item( i )->GetId() );
      m_pCommandsTree->AppendItem( rootid, arr->Item( i )->GetName(), -1, -1, treedata );
    }
    m_pCommandsTree->Expand( m_pCommandsTree->GetRootItem() );
  } else {
    const wxCmdArray *arr = toimport.GetArray();
    for( int i = 0; i < ( int )arr->GetCount(); i++ ) {
      m_pCommandsList->Append( arr->Item( i )->GetName(), reinterpret_cast<void*>( arr->Item( i )->GetId() ) );
    }
    m_pCategories->Append( wxT( "Generic" ) );
  }
}

void wxKeyConfigPanel::AddProfile( const wxKeyProfile &p ) {
  m_pKeyProfiles->Append( p.GetName(), ( void * )( new wxKeyProfile( p ) ) );
  if( m_pKeyProfiles->GetCount() == 1 ) {
    SetSelProfile( 0 );
  }
}

void wxKeyConfigPanel::AddProfiles( const wxKeyProfileArray &arr ) {
  for( int i = 0; i < arr.GetCount(); i++ ) {
    wxKeyProfile *copy = new wxKeyProfile( *arr.Item( i ) );
    m_pKeyProfiles->Append( arr.Item( i )->GetName(), ( void * )copy );
  }
  SetSelProfile( arr.GetSelProfileIdx() >= 0 ? arr.GetSelProfileIdx() : 0 );
}

void wxKeyConfigPanel::SetSelProfile( int n ) {
  wxASSERT( m_pKeyProfiles && n >= 0 && n < static_cast<int>( m_pKeyProfiles->GetCount() ) );
  m_pKeyProfiles->SetSelection( n );
  m_nCurrentProf = n;
  wxCommandEvent ev;
  OnProfileSelected( ev );
}

void wxKeyConfigPanel::Reset() {
  if( IsUsingTreeCtrl() ) {
    m_pCommandsTree->DeleteAllItems();
  } else {
    m_pCommandsList->Clear();
    m_pCategories->Clear();
  }
  m_pBindings->Clear();
  m_pDescLabel->Clear();
  m_pKeyField->Clear();
}

void wxKeyConfigPanel::AddRootIfMissing( const wxString &rootname ) {
  wxASSERT( IsUsingTreeCtrl() );
  if( !m_pCommandsTree->GetRootItem().IsOk() ) {
    m_pCommandsTree->DeleteAllItems();
    m_pCommandsTree->AddRoot( rootname );
  }
}

wxCmd *wxKeyConfigPanel::GetSelCmd() const {
  intptr_t id;
  if( IsUsingTreeCtrl() ) {
    wxTreeItemId treeid = GetSelCmdId();
    if( !treeid.IsOk() ) {
      return NULL;
    }
    id  = ( ( wxExTreeItemData * )m_pCommandsTree->GetItemData( treeid ) )->GetMenuItemId();
  } else {
    int sel = m_pCommandsList->GetSelection();
    if( sel < 0 ) {
      return NULL;
    }
    id = ( intptr_t )m_pCommandsList->GetClientData( sel );
  }
  return m_kBinder.GetCmd( id );
}

wxTreeItemId wxKeyConfigPanel::GetSelCmdId() const {
  wxTreeItemId selection = m_pCommandsTree->GetSelection();
  if( !selection.IsOk() ) {
    return selection;
  }
  wxTreeItemData *p = m_pCommandsTree->GetItemData( selection );
  if( p == NULL ) {
    return wxTreeItemId();
  }
  if( m_pCommandsTree->ItemHasChildren( selection ) ) {
    return wxTreeItemId();
  }
  return selection;
}

wxControl *wxKeyConfigPanel::GetMainCtrl() const {
  if( m_nBuildMode & wxKEYBINDER_USE_TREECTRL ) {
    return ( wxControl* )m_pCommandsTree;
  }
  return m_pCommandsList;
}

wxString wxKeyConfigPanel::GetSelCmdStr() const {
  wxTreeItemId id = GetSelCmdId();
  if( !id.IsOk() ) {
    return wxEmptyString;
  }
  return m_pCommandsTree->GetItemText( id );
}

bool wxKeyConfigPanel::IsSelectedValidCmd() const {
  if( IsUsingTreeCtrl() ) {
    return GetSelCmdId().IsOk();
  } else {
    return m_pCommandsList->GetSelection() >= 0;
  }
}

wxKeyProfileArray wxKeyConfigPanel::GetProfiles() const {
  wxKeyProfileArray arr;
  for( size_t i = 0; i < m_pKeyProfiles->GetCount(); i++ ) {
    arr.Add( new wxKeyProfile( *GetProfile( i ) ) );
  }
  arr.SetSelProfile( GetSelProfileIdx() );
  return arr;
}

void wxKeyConfigPanel::UpdateButtons() {
  wxString str;
  m_pRemoveBtn->Enable( m_pBindings->GetSelection() != wxNOT_FOUND );
  m_pRemoveAllBtn->Enable( m_pBindings->GetCount() > 0 );
  bool b = IsSelectedValidCmd() && m_pKeyField->IsValidKeyComb();
  m_pAssignBtn->Enable( b );
  if( m_pKeyField->IsValidKeyComb() ) {
    wxCmd *p = m_kBinder.GetCmdBindTo( m_pKeyField->GetValue() );
    if( p ) {
      m_pCurrCmd = p;
      str = p->GetName();
    } else {
      str = wxT( "None" );
      m_pCurrCmd = NULL;
    }
  }
  m_pCurrCmdField->SetLabel( str );
}

void wxKeyConfigPanel::UpdateDesc() {
  wxCmd *p = GetSelCmd();
  if( p != NULL ) {
    m_pDescLabel->SetValue( p->GetDescription() );
  } else {
    m_pDescLabel->SetLabel( wxT( "" ) );
  }
}

void wxKeyConfigPanel::FillInBindings() {
  m_pBindings->Clear();
  wxCmd *p = GetSelCmd();
  if( p == NULL ) {
    return;
  }
  m_pBindings->Append( p->GetShortcutsList() );
  if( m_pBindings->GetCount() > 0 ) {
    m_pBindings->SetSelection( 0 );
  }
}

void wxKeyConfigPanel::ApplyChanges() {
  wxKeyProfile *prof = GetSelProfile();
  wxASSERT( prof );
  prof->DeepCopy( m_kBinder );
  m_pKeyProfiles->SetString( GetSelProfileIdx(), m_kBinder.GetName() );
}

void wxKeyConfigPanel::EnableKeyProfiles( bool bEnable ) {
  m_bEnableKeyProfiles = bEnable;
  ShowSizer( m_pKeyProfilesSizer, m_bEnableKeyProfiles );
}

void wxKeyConfigPanel::ShowSizer( wxSizer *toshow, bool show ) {
  toshow->ShowItems( m_bEnableKeyProfiles );
  wxSizer *main = GetSizer();
  bool oldshow = main->IsShown( toshow );
  if( ( show && oldshow ) || ( !show && !oldshow ) ) {
    return;
  }
  if( show ) {
    main->Prepend( toshow, 0, wxGROW );
  } else {
    main->Detach( toshow );
  }
  m_minHeight = m_maxHeight = -1;
  wxSize sizeTotal = GetSize(), sizeClient = GetClientSize();
  wxSize size = main->GetMinSize();
  size.x += sizeTotal.x - sizeClient.x;
  size.y += sizeTotal.y - sizeClient.y;
  if( !show ) {
    m_maxHeight = size.y;
  }
  SetSizeHints( size.x, size.y, m_maxWidth, m_maxHeight );
  SetSize( wxDefaultCoord, size.y );
  Show();
}

void wxKeyConfigPanel::OnApplyChanges( wxCommandEvent &event ) {
  ApplyChanges();
  event.Skip();
}

void wxKeyConfigPanel::OnProfileEditing( wxCommandEvent & ) {
  wxASSERT( m_nCurrentProf != -1 );
  return ;
  wxString oldname = m_kBinder.GetName();
  if( oldname == wxEmptyString ) {
    return;
  }
  wxString newname = m_pKeyProfiles->GetValue();
  if( newname == oldname ) {
    return;
  }
  m_bProfileHasBeenModified = TRUE;
  m_kBinder.SetName( newname );
}

void wxKeyConfigPanel::OnTreeCommandSelected( wxTreeEvent & ) {
  FillInBindings();
  UpdateButtons();
  UpdateDesc();
}

void wxKeyConfigPanel::OnListCommandSelected( wxCommandEvent & ) {
  FillInBindings();
  UpdateButtons();
  UpdateDesc();
}

void wxKeyConfigPanel::OnBindingSelected( wxCommandEvent & ) {
  UpdateButtons();
}

void wxKeyConfigPanel::OnCategorySelected( wxCommandEvent &ev ) {
  int sel = m_pCategories->GetSelection();
  if( sel == -1 ) {
    return;
  }
  wxExComboItemData *data = ( wxExComboItemData* )m_pCategories->GetClientObject( sel );
  wxArrayString &arr = data->GetCmdNameArr();
  m_pCommandsList->Clear();
  for( int i = 0; i < ( int )arr.GetCount(); i++ ) {
    m_pCommandsList->Append( arr.Item( i ), reinterpret_cast<void *>( data->GetID( i ) ) );
  }
  m_pCommandsList->Select( 0 );
  OnListCommandSelected( ev );
}

void wxKeyConfigPanel::OnProfileSelected( wxCommandEvent & ) {
  int selidx = m_pKeyProfiles->GetSelection();
  wxKeyProfile *sel;
  if( selidx != -1 ) {
    if( m_bProfileHasBeenModified ) {
      m_pKeyProfiles->SetString( m_nCurrentProf, GetProfile( m_nCurrentProf )->GetName() );
    }
    m_nCurrentProf = selidx;
    sel = GetProfile( m_nCurrentProf );
  } else {
    sel = GetSelProfile();
  }
  if( !sel ) {
    return;
  }
  m_kBinder.DeepCopy( *sel );
  m_bProfileHasBeenModified = FALSE;
  if( IsUsingTreeCtrl() ) {
    wxTreeEvent ev;
    OnTreeCommandSelected( ev );
  } else {
    wxCommandEvent ev;
    OnListCommandSelected( ev );
  }
}

void wxKeyConfigPanel::OnAssignKey( wxCommandEvent & ) {
  wxASSERT( m_pKeyField->IsValidKeyComb() );
  wxCmd *sel = GetSelCmd();
  if( !sel ) {
    wxMessageBox( wxT( "KeyBinding file corrupted. Please delete\n" ) + *pKeyFilename );
    return;
  }
  if( sel->GetShortcutCount() >= wxCMD_MAX_SHORTCUTS ) {
    wxMessageBox( wxString::Format( wxT( "Cannot add more than %d shortcuts to a single command..." ), wxCMD_MAX_SHORTCUTS ),
                  wxT( "Cannot add another shortcut" ) );
    return;
  }
  wxCmd *p = 0;
  while( ( p = m_kBinder.GetCmdBindTo( m_pKeyField->GetValue() ) ) ) {
    wxKeyBind tmp( m_pKeyField->GetValue() );
    int n;
    if( p->IsBindTo( tmp, &n ) ) {
      p->RemoveShortcut( n );
    }
  }
  sel->AddShortcut( m_pKeyField->GetValue() );
  m_bProfileHasBeenModified = TRUE;
  FillInBindings();
  m_pKeyField->Clear();
}

void wxKeyConfigPanel::OnRemoveKey( wxCommandEvent & ) {
  int selection = m_pBindings->GetSelection();
  if( selection == wxNOT_FOUND ) {
    return;
  }
  m_bProfileHasBeenModified = TRUE;
  GetSelCmd()->RemoveShortcut( selection );
  FillInBindings();
  UpdateButtons();
}

void wxKeyConfigPanel::OnRemoveAllKey( wxCommandEvent & ) {
  m_bProfileHasBeenModified = TRUE;
  GetSelCmd()->RemoveAllShortcuts();
  FillInBindings();
  UpdateButtons();
}

void wxKeyConfigPanel::OnAddProfile( wxCommandEvent & ) {
  wxKeyProfile *sel = GetSelProfile();
  if( !sel ) {
    return;
  }
  wxTextEntryDialog dlg( this, wxT( "Input the name of the new profile.\n" )
                         wxT( "The new profile will be initially set to a copy of the last selected profile." ),
                         wxT( "Add new profile" ) );
  dlg.SetValue( sel->GetName() );
  bool valid = FALSE;
  while( !valid ) {
    if( dlg.ShowModal() == wxID_CANCEL ) {
      return;
    }
    valid = TRUE;
    for( size_t j = 0; j < m_pKeyProfiles->GetCount(); j++ ) {
      valid &= ( GetProfile( j )->GetName() != dlg.GetValue() );
    }
    if( !valid ) {
      wxMessageBox( wxT( "The given profile name is already in use.\n Enter another name." ) );
    }
  }
  wxKeyProfile *newprof = new wxKeyProfile( *sel );
  newprof->SetName( dlg.GetValue() );
  AddProfile( *newprof );
  delete newprof;
  SetSelProfile( m_pKeyProfiles->GetCount() - 1 );
}

void wxKeyConfigPanel::OnRemoveProfile( wxCommandEvent & ) {
  wxASSERT( m_nCurrentProf != -1 );
  if( m_pKeyProfiles->GetCount() == 1 ) {
    wxMessageBox( wxT( "Cannot delete this profile. It's the only available profile." ), wxT( "Warning" ) );
    return;
  }
  delete( ( wxKeyProfile* )m_pKeyProfiles->GetClientData( m_nCurrentProf ) );
  m_pKeyProfiles->Delete( m_nCurrentProf );
  int newsel = m_nCurrentProf - 1;
  if( newsel < 0 ) {
    newsel = 0;
  }
  wxASSERT( newsel < static_cast<int>( m_pKeyProfiles->GetCount() ) );
  SetSelProfile( newsel );
}

void wxKeyConfigPanel::OnKeyPressed( wxCommandEvent & ) {
  UpdateButtons();
}
